﻿/*
____
 L|ypps Web controls for ASP.NET

Copyright (c) 2009 Alessandro Zifiglio, http://www.Typps.com
EMail: alessandro@Typps.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Typps
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Data;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.Caching;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;

    public class WebResourceHandler : IHttpHandler
    {
        #region Fields

        private static object _timingLock = new Object();

        private int _maximumDimension = 100;

        #endregion Fields

        #region Properties

        // Override the IsReusable property.
        public bool IsReusable
        {
            get { return false; }
        }

        #endregion Properties

        #region Methods

        public void ProcessRequest(HttpContext context)
        {
            string resourceName = context.Request.QueryString["d"];
            if (!string.IsNullOrEmpty(resourceName))
            {
                RenderResource(context, resourceName);
            }
            else
            {
                string hashedQueryString = context.Request.QueryString["h"];
                if (!string.IsNullOrEmpty(hashedQueryString))
                {
                    NameValueCollection nvc = HttpUtility.ParseQueryString(Helper.DecodeFrom64(hashedQueryString));
                    string req = nvc["d"];
                    switch (req)
                    {
                        case "tg":
                            nvc.Add("imgurl",context.Request.QueryString["imgurl"]);
                            RenderThumbnail(context, nvc);
                            break;
                        case "css":
                            RenderCss(context, nvc);
                            break;
                    }
                }
            }
        }

        public void RenderCss(HttpContext context, NameValueCollection nvc)
        {
            string css = string.Empty;

            ControlName ctlName = (ControlName)Enum.Parse(typeof(ControlName),
                (string)nvc["ctl"] ?? ControlName.Editor.ToString());

            switch (ctlName)
            {
                case ControlName.Editor:
                    css = StyleUtility.GetEditorStyles(context, nvc);
                    break;
                case ControlName.ImagePicker:
                    css = StyleUtility.GetImagePickerStyles(context, nvc);
                    break;
                case ControlName.ColorPicker:
                    css = StyleUtility.GetColorPickerStyles(context, nvc);
                    break;
                case ControlName.UpdateProgressPanel:
                    css = StyleUtility.GetUpdatePanelProgressStyles(context, nvc);
                    break;
                case ControlName.MediaFrame:
                    css = StyleUtility.GetMediaFrameStyles(context, nvc);
                    break;
                case ControlName.SlidePane:
                    css = StyleUtility.GetSlidePaneStyles(context, nvc);
                    break;
            }

            if (!string.IsNullOrEmpty(css))
            {
                context.Response.ContentType = "text/css";
                context.Response.AddHeader("Content-Disposition", "inline;filename=typps");
                context.Response.AddHeader("Content-Length", css.Length.ToString());
                context.Response.Write(css);
            }
            context.Response.End();
        }

        public void RenderResource(HttpContext context, string resourceName)
        {
            if (!string.IsNullOrEmpty(resourceName))
            {
                Assembly a = typeof(Editor).Assembly;
                string resourcePath = string.Format("{0}.{1}", typeof(Editor).Namespace, resourceName);
                byte[] buffer = null;
                int len = 0;
                using (Stream sr =
                    a.GetManifestResourceStream(resourcePath))
                {
                    if (sr != null)
                    {
                        len = (int)sr.Length;
                        buffer = new byte[len];
                        sr.Read(buffer, 0, len);
                    }
                }
                object[] attributes = a.GetCustomAttributes(false);
                string contentType = string.Empty;
                for (int num1 = 0; num1 < attributes.Length; num1++)
                {
                    WebResourceAttribute attr = attributes[num1] as WebResourceAttribute;
                    if ((attr != null) &&
                        attr.WebResource.Equals(resourcePath,
                        StringComparison.OrdinalIgnoreCase))
                    {
                        contentType = attr.ContentType;
                        break;
                    }
                }
                context.Response.ContentType = contentType;
                context.Response.AddHeader("Content-Disposition", string.Format("inline;filename={0}", resourcePath));
                context.Response.AddHeader("Content-Length", len.ToString());
                context.Response.BinaryWrite(buffer);
                context.Response.End();
            }
        }

        public void RenderThumbnail(HttpContext context, NameValueCollection nvc)
        {
            int width;
            int.TryParse((string)(nvc["width"] ?? string.Empty), out width);
            if (width > 0)
                _maximumDimension = width;
            string url = (string)nvc["imgurl"];
            if (string.IsNullOrEmpty(url))
                return;
            url = context.Server.UrlDecode(url);
            HttpRequest request = context.Request;
            Uri uriToBitmap = new Uri(url, UriKind.RelativeOrAbsolute);
            Uri originalUri = request.Url;
            UriKind kind = UriKind.Relative;
            if (uriToBitmap.IsAbsoluteUri)
                kind = UriKind.Absolute;
            DateTime lastModified = DateTime.MinValue;
            if (kind == UriKind.Relative)
            {
                url = context.Server.MapPath(url);
                lastModified = File.GetLastWriteTime(url);
            }

            /*Many thanks to Bertrand Le Roy --the .NET ninja at microsoft!
             -------------------------------------------------------------*/

            CompositingQuality compositingQuality = CompositingQuality.Default;
            InterpolationMode interpolationMode = InterpolationMode.Default;
            try
            {
                compositingQuality =
                    (CompositingQuality)Enum.Parse(typeof(CompositingQuality), nvc["cq"], true);
                interpolationMode =
                    (InterpolationMode)Enum.Parse(typeof(InterpolationMode), nvc["im"], true);
            }
            catch (ArgumentException)
            {
            }

            // Build the thumbnail and send it to the output stream
            using (Stream stream = Helper.GetStreamFromUrl(url, kind, context.Server))
            {
                if (stream == null)
                {
                    //we'd never get this far if there was really an error, but so what!
                    throw new HttpException(404, "Photo not found");
                }
                Bitmap target = null;
                try
                {
                    using (var bitmap = new Bitmap(stream))
                    {
                        var size = bitmap.PhysicalDimension;
                        var isWide = size.Width > size.Height;
                        var smallestDimension = isWide ? size.Height : size.Width;
                        var targetSize = new SizeF(
                            _maximumDimension * size.Width / smallestDimension,
                            _maximumDimension * size.Height / smallestDimension);
                        target = new Bitmap(_maximumDimension, _maximumDimension);
                        // Introducing a lock for more reliable timing. Don't do this at home
                        lock (_timingLock)
                        {
                            DateTime before = DateTime.Now;
                            for (int i = 0; i < 100; i++)
                            {
                                using (var graphics = Graphics.FromImage(target))
                                {
                                    graphics.CompositingQuality = compositingQuality;
                                    graphics.InterpolationMode = interpolationMode;
                                    graphics.CompositingMode = CompositingMode.SourceCopy;
                                    graphics.DrawImage(bitmap,
                                        (_maximumDimension - targetSize.Width) / 2,
                                        (_maximumDimension - targetSize.Height) / 2,
                                        targetSize.Width, targetSize.Height);
                                }
                            }
                            DateTime after = DateTime.Now;
                            // Ticks are 100ns each, we did the job 100 time so we get milliseconds by dividing by 1000000,
                            double timeTaken = (after.Ticks - before.Ticks) / 1000000.0;
                            context.Application["PhotoThumbnail_" + url + compositingQuality + interpolationMode] =
                                timeTaken;
                        }
                    }
                    // Setup caching
                    HttpCachePolicy cachePolicy = context.Response.Cache;
                    cachePolicy.SetCacheability(HttpCacheability.Public);
                    cachePolicy.VaryByParams["imgurl"] = true;
                    cachePolicy.SetOmitVaryStar(true);
                    cachePolicy.SetExpires(DateTime.Now + TimeSpan.FromDays(365));
                    cachePolicy.SetValidUntilExpires(true);
                    if(lastModified!=DateTime.MinValue)
                        cachePolicy.SetLastModified(lastModified);
                    // Content-type
                    context.Response.ContentType = "image/png";
                    // Send the contents (need to use a searchable intermediary stream for png
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        target.Save(memoryStream, ImageFormat.Png);
                        memoryStream.WriteTo(context.Response.OutputStream);
                    }
                }
                finally
                {
                    if (target != null)
                    {
                        target.Dispose();
                    }
                }
            }
        }

        #endregion Methods
    }
}